home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 2002 November / SGI Freeware 2002 November - Disc 2.iso / dist / fw_guile.idb / usr / freeware / share / guile / 1.5.6 / scripts / frisk.z / frisk
Text File  |  2002-07-08  |  12KB  |  293 lines

  1. #!/bin/sh
  2. # aside from this initial boilerplate, this is actually -*- scheme -*- code
  3. main='(module-ref (resolve-module '\''(scripts frisk)) '\'main')'
  4. exec ${GUILE-guile} -l $0 -c "(apply $main (cdr (command-line)))" "$@"
  5. !#
  6. ;;; frisk --- Grok the module interfaces of a body of files
  7.  
  8. ;;     Copyright (C) 2002 Free Software Foundation, Inc.
  9. ;;
  10. ;; This program is free software; you can redistribute it and/or
  11. ;; modify it under the terms of the GNU General Public License as
  12. ;; published by the Free Software Foundation; either version 2, or
  13. ;; (at your option) any later version.
  14. ;;
  15. ;; This program is distributed in the hope that it will be useful,
  16. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  18. ;; General Public License for more details.
  19. ;;
  20. ;; You should have received a copy of the GNU General Public License
  21. ;; along with this software; see the file COPYING.  If not, write to
  22. ;; the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  23. ;; Boston, MA 02111-1307 USA
  24.  
  25. ;;; Author: Thien-Thi Nguyen <ttn@gnu.org>
  26.  
  27. ;;; Commentary:
  28.  
  29. ;; Usage: frisk [options] file ...
  30. ;;
  31. ;; Analyze FILE... module interfaces in aggregate (as a "body"),
  32. ;; and display a summary.  Modules that are `define-module'd are
  33. ;; considered "internal" (and those not, "external").  When module X
  34. ;; uses module Y, X is said to be "(a) downstream of" Y, and Y is
  35. ;; "(an) upstream of" X.
  36. ;;
  37. ;; Normally, the summary displays external modules and their internal
  38. ;; downstreams, as this is the usual question asked by a body.  There
  39. ;; are several options that modify this output.
  40. ;;
  41. ;;  -u, --upstream      show upstream edges
  42. ;;  -d, --downstream    show downstream edges (default)
  43. ;;  -i, --internal      show internal modules
  44. ;;  -x, --external      show external modules (default)
  45. ;;
  46. ;; If given both `upstream' and `downstream' options ("frisk -ud"), the
  47. ;; output is formatted: "C MODULE --- UP-LS --- DOWN-LS", where C is
  48. ;; either `i' or `x', and each element of UP-LS and DOWN-LS is (TYPE
  49. ;; MODULE-NAME ...).
  50. ;;
  51. ;; In all other cases, the "C MODULE" occupies its own line, and
  52. ;; subsequent lines list the up- or downstream edges, respectively,
  53. ;; indented by some non-zero amount of whitespace.
  54. ;;
  55. ;; Top-level `use-modules' (or `load' or 'primitive-load') forms in a
  56. ;; file that do not follow a `define-module' result an edge where the
  57. ;; downstream is the "default module", normally `(guile-user)'.  This
  58. ;; can be set to another value by using:
  59. ;;
  60. ;;  -m, --default-module MOD    set MOD as the default module
  61.  
  62. ;; Usage from a Scheme Program: (use-modules (scripts frisk))
  63. ;;
  64. ;; Module export list:
  65. ;;  (frisk . args)
  66. ;;  (make-frisker . options)    => (lambda (files) ...) [see below]
  67. ;;  (mod-up-ls module)          => upstream edges
  68. ;;  (mod-down-ls module)        => downstream edges
  69. ;;  (mod-int? module)           => is the module internal?
  70. ;;  (edge-type edge)            => symbol: {regular,autoload,computed}
  71. ;;  (edge-up edge)              => upstream module
  72. ;;  (edge-down edge)            => downstream module
  73. ;;
  74. ;; OPTIONS is an alist.  Recognized keys are:
  75. ;;  default-module
  76. ;;
  77. ;; `make-frisker' returns a procedure that takes a list of files, the
  78. ;; FRISKER.  FRISKER returns a closure, REPORT, that takes one of the
  79. ;; keys:
  80. ;;  modules  -- entire list of modules
  81. ;;  internal -- list of internal modules
  82. ;;  external -- list of external modules
  83. ;;  i-up     -- list of modules upstream of internal modules
  84. ;;  x-up     -- list of modules upstream of external modules
  85. ;;  i-down   -- list of modules downstream of internal modules
  86. ;;  x-down   -- list of modules downstream of external modules
  87. ;;  edges    -- list of edges
  88. ;; Note that `x-up' should always be null, since by (lack of!)
  89. ;; definition, we only know external modules by reference.
  90. ;;
  91. ;; The module and edge objects managed by REPORT can be examined in
  92. ;; detail by using the other (self-explanatory) procedures.  Be careful
  93. ;; not to confuse a freshly consed list of symbols, like `(a b c)' with
  94. ;; the module `(a b c)'.  If you want to find the module by that name,
  95. ;; try: (cond ((member '(a b c) (REPORT 'modules)) => car)).
  96.  
  97. ;; TODO: Make "frisk -ud" output less ugly.
  98. ;;       Consider default module as internal; add option to invert.
  99. ;;       Support `edge-misc' data.
  100.  
  101. ;;; Code:
  102.  
  103. (define-module (scripts frisk)
  104.   :autoload (ice-9 getopt-long) (getopt-long)
  105.   :use-module ((srfi srfi-1) :select (filter remove))
  106.   :export (frisk
  107.            make-frisker
  108.            mod-up-ls mod-down-ls mod-int?
  109.            edge-type edge-up edge-down))
  110.  
  111. (define *default-module* '(guile-user))
  112.  
  113. (define (grok-proc default-module note-use!)
  114.   (lambda (filename)
  115.     (let* ((p (open-file filename "r"))
  116.            (next (lambda () (read p)))
  117.            (ferret (lambda (use)   ;;; handle "((foo bar) :select ...)"
  118.                      (let ((maybe (car use)))
  119.                        (if (list? maybe)
  120.                            maybe
  121.                            use))))
  122.            (curmod #f))
  123.       (let loop ((form (next)))
  124.         (cond ((eof-object? form))
  125.               ((not (list? form)) (loop (next)))
  126.               (else (case (car form)
  127.                       ((define-module)
  128.                        (let ((module (cadr form)))
  129.                          (set! curmod module)
  130.                          (note-use! 'def module #f)
  131.                          (let loop ((ls form))
  132.                            (or (null? ls)
  133.                                (case (car ls)
  134.                                  ((:use-module)
  135.                                   (note-use! 'regular module (ferret (cadr ls)))
  136.                                   (loop (cddr ls)))
  137.                                  ((:autoload)
  138.                                   (note-use! 'autoload module (cadr ls))
  139.                                   (loop (cdddr ls)))
  140.                                  (else (loop (cdr ls))))))))
  141.                       ((use-modules)
  142.                        (for-each (lambda (use)
  143.                                    (note-use! 'regular
  144.                                               (or curmod default-module)
  145.                                               (ferret use)))
  146.                                  (cdr form)))
  147.                       ((load primitive-load)
  148.                        (note-use! 'computed
  149.                                   (or curmod default-module)
  150.                                   (let ((file (cadr form)))
  151.                                     (if (string? file)
  152.                                         file
  153.                                         (format #f "[computed in ~A]"
  154.                                                 filename))))))
  155.                     (loop (next))))))))
  156.  
  157. (define up-ls (make-object-property))   ; list
  158. (define dn-ls (make-object-property))   ; list
  159. (define int?  (make-object-property))   ; defined via `define-module'
  160.  
  161. (define mod-up-ls up-ls)
  162. (define mod-down-ls dn-ls)
  163. (define mod-int? int?)
  164.  
  165. (define (i-or-x module)
  166.   (if (int? module) 'i 'x))
  167.  
  168. (define edge-type (make-object-property)) ; symbol
  169.  
  170. (define (make-edge type up down)
  171.   (let ((new (cons up down)))
  172.     (set! (edge-type new) type)
  173.     new))
  174.  
  175. (define edge-up car)
  176. (define edge-down cdr)
  177.  
  178. (define (up-ls+! m new) (set! (up-ls m) (cons new (up-ls m))))
  179. (define (dn-ls+! m new) (set! (dn-ls m) (cons new (dn-ls m))))
  180.  
  181. (define (make-body alist)
  182.   (lambda (key)
  183.     (assq-ref alist key)))
  184.  
  185. (define (scan default-module files)
  186.   (let* ((modules (list))
  187.          (edges (list))
  188.          (intern (lambda (module)
  189.                    (cond ((member module modules) => car)
  190.                          (else (set! (up-ls module) (list))
  191.                                (set! (dn-ls module) (list))
  192.                                (set! modules (cons module modules))
  193.                                module))))
  194.          (grok (grok-proc default-module
  195.                           (lambda (type d u)
  196.                             (let ((d (intern d)))
  197.                               (if (eq? type 'def)
  198.                                   (set! (int? d) #t)
  199.                                   (let* ((u (intern u))
  200.                                          (edge (make-edge type u d)))
  201.                                     (set! edges (cons edge edges))
  202.                                     (up-ls+! d edge)
  203.                                     (dn-ls+! u edge))))))))
  204.     (for-each grok files)
  205.     (make-body
  206.      `((modules  . ,modules)
  207.        (internal . ,(filter int? modules))
  208.        (external . ,(remove int? modules))
  209.        (i-up     . ,(filter int? (map edge-down edges)))
  210.        (x-up     . ,(remove int? (map edge-down edges)))
  211.        (i-down   . ,(filter int? (map edge-up   edges)))
  212.        (x-down   . ,(remove int? (map edge-up   edges)))
  213.        (edges    . ,edges)))))
  214.  
  215. (define (make-frisker . options)
  216.   (let ((default-module (or (assq-ref options 'default-module)
  217.                             *default-module*)))
  218.     (lambda (files)
  219.       (scan default-module files))))
  220.  
  221. (define (dump-updown modules)
  222.   (for-each (lambda (m)
  223.               (format #t "~A ~A --- ~A --- ~A\n"
  224.                       (i-or-x m) m
  225.                       (map (lambda (edge)
  226.                              (cons (edge-type edge)
  227.                                    (edge-up edge)))
  228.                            (up-ls m))
  229.                       (map (lambda (edge)
  230.                              (cons (edge-type edge)
  231.                                    (edge-down edge)))
  232.                            (dn-ls m))))
  233.             modules))
  234.  
  235. (define (dump-up modules)
  236.   (for-each (lambda (m)
  237.               (format #t "~A ~A\n" (i-or-x m) m)
  238.               (for-each (lambda (edge)
  239.                           (format #t "\t\t\t ~A\t~A\n"
  240.                                   (edge-type edge) (edge-up edge)))
  241.                         (up-ls m)))
  242.             modules))
  243.  
  244. (define (dump-down modules)
  245.   (for-each (lambda (m)
  246.               (format #t "~A ~A\n" (i-or-x m) m)
  247.               (for-each (lambda (edge)
  248.                           (format #t "\t\t\t ~A\t~A\n"
  249.                                   (edge-type edge) (edge-down edge)))
  250.                         (dn-ls m)))
  251.             modules))
  252.  
  253. (define (frisk . args)
  254.   (let* ((parsed-opts (getopt-long
  255.                        (cons "frisk" args)    ;;; kludge
  256.                        '((upstream (single-char #\u))
  257.                          (downstream (single-char #\d))
  258.                          (internal (single-char #\i))
  259.                          (external (single-char #\x))
  260.                          (default-module
  261.                            (single-char #\m)
  262.                            (value #t)))))
  263.          (=u (option-ref parsed-opts 'upstream #f))
  264.          (=d (option-ref parsed-opts 'downstream #f))
  265.          (=i (option-ref parsed-opts 'internal #f))
  266.          (=x (option-ref parsed-opts 'external #f))
  267.          (files    (option-ref parsed-opts '() (list)))
  268.          (report   ((make-frisker
  269.                      `(default-module
  270.                         . ,(option-ref parsed-opts 'default-module
  271.                                        *default-module*)))
  272.                     files))
  273.          (modules  (report 'modules))
  274.          (internal (report 'internal))
  275.          (external (report 'external))
  276.          (edges    (report 'edges)))
  277.     (format #t "~A ~A, ~A ~A (~A ~A, ~A ~A), ~A ~A\n\n"
  278.             (length files)    "files"
  279.             (length modules)  "modules"
  280.             (length internal) "internal"
  281.             (length external) "external"
  282.             (length edges)    "edges")
  283.     ((cond ((and =u =d) dump-updown)
  284.            (=u dump-up)
  285.            (else dump-down))
  286.      (cond ((and =i =x) modules)
  287.            (=i internal)
  288.            (else external)))))
  289.  
  290. (define main frisk)
  291.  
  292. ;;; frisk ends here
  293.